<?php
class Form
{
	public $id, $fields, $action='', $method, $submitted, $request, $errors, $valid;
	
	private $custom = false;
	
	public function __construct($id, $action='', $method='POST')
	{
		$this->id = $id;
		$this->fields = array();
		$this->action = ($action == '')?$_SERVER['REQUEST_URI']:$action;
		$this->method = $method;
		
		if($this->submitted = ($method == 'POST')?isset($_POST[$this->id]):isset($_GET[$this->id]))
		{
			$this->errors = array();
			$this->request = ($method == 'POST')?array_merge($_POST, $_FILES):$_GET;
			
			if(isset($this->request['custom']))
			{
				$this->custom = true;
			}
		}
	}
	
	// Script
	public function script()
	{
		Template::script('Form');
		
		Template::script_code('new Form("'.$this->id.'", "'.$this->action.'", "'.$this->method.'", {');
		
		foreach($this->fields as $field)
		{
			Template::script_code('"'.$field[0].'": {"type":"'.$field[1].'", "validation": {');
			
			foreach($field[2] as $key=>$value)
			{
				Template::script_code('"'.$key.'": ["'.$this->error_message($field[0], $key, $value).'", '.json_encode($value).'],');
			}
			
			Template::script_code('}},');
		}
		
		Template::script_code('});');
	}
	
	// Field
	public function field($name, $type='text', $validation=array())
	{
		if($this->custom && $this->request['name'] == $name)
		{
			if(isset($validation[$this->request['validator']]))
			{
				die(call_user_func($validation[$this->request['validator']], $this->request['value'], $this->request['fields'], $this->id)?'1':'0');
			}
		}
		
		$this->fields[$name] = func_get_args();
		
		switch($type)
		{
			case 'password':
				if(isset($validation['confirm']) && $validation['confirm'])
				{
					unset($this->fields[$name][2]['confirm']);
					
					$this->fields[$name.'_confirm'] = array($name.'_confirm', 'password', array('confirm'=>$name));
				}
				break;
		}
	}
	
	// Lang
	private function lang()
	{
		$args = array_merge(array($this->id), func_get_args());
		$length = count($args);
		
		for($i=0; $i<$length; $i++)
		{
			$key = implode('_', array_slice($args, $i));
			
			if(isset(fw::$lang[$key])) return fw::$lang[$key];
		}
		
		return $args[$length - 1];
	}
	
	private function error_message($field, $validator, $data)
	{
		$lang = $this->lang($field, 'error_'.$validator);
		
		switch($validator)
		{
			case 'min_length':
			case 'max_length':
				$lang = lang($lang, $data, false, false);
				break;
		}
		
		return $lang;
	}
	
	// Output
	public function output($custom=array())
	{
		$config = array_merge(array
		(
			'errors'	=>	'inline'
		), $custom);
		
		echo '<form id="'.$this->id.'" method="'.$this->method.'" action="'.$this->action.'" enctype="multipart/form-data">';
		
		foreach($this->fields as $field)
		{
			echo ($this->submitted && isset($this->errors[$field[0]]))?'<p class="invalid">':'<p>';
			
			# Label
			$label = $this->lang($field[0]);
			
			if($config['errors'] == 'label')
			{
				if($this->submitted && isset($this->errors[$field[0]]))
				{
					echo '<label title="'.$label.'" id="'.$this->id.'_'.$field[0].'_error">'.$this->errors[$field[0]].'</label>';
				} else
				{
					echo '<label title="'.$label.'" id="'.$this->id.'_'.$field[0].'_error">'.$label.'</label>';
				}
			} else
			{
				echo '<label title="'.$label.'">'.$label.'</label>';
			}
			
			# Input
			switch($field[1])
			{
				case 'text':
				case 'password':
					if($this->submitted)
					{
						echo '<input id="'.$this->id.'_'.$field[0].'" class="text" type="'.$field[1].'" name="'.$field[0].'" value="'.htmlentities(utf8_decode($this->request[$field[0]]), ENT_QUOTES).'"/>';
					} else
					{
						echo '<input id="'.$this->id.'_'.$field[0].'" class="text" type="'.$field[1].'" name="'.$field[0].'"'.(isset($field[3])?' value="'.$field[3].'"':'').'/>';
					}
					break;
				case 'select':
					echo '<select id="'.$this->id.'_'.$field[0].'" name="'.$field[0].'">';
					
					foreach($field[2] as $key=>$value)
					{
						echo '<option value="'.$key.'"';
						
						if($this->submitted && $this->request[$field[0]] == $key)
						{
							echo ' selected="selected"';
						} elseif(isset($field[3]) && $field[3] == $key)
						{
							echo ' selected="selected"';
						}
						
						echo '>'.$value.'</option>';
					}
					
					echo '</select>';
					break;
			}
			
			echo '</p>';
		}
		
		# Submit
		echo '<p class="submit">';
		
		echo '<input id="'.$this->id.'_submit" class="button enabled" type="submit" name="'.$this->id.'" value="'.$this->lang('submit').'"/>';
		
		if(isset($config['submit_right']))
		{
			echo $config['submit_right'];
		}
		
		echo '</p>';
		
		echo '</form>';
	}
	
	// Validate
	public function validate()
	{
		$this->script();
		
		if(!$this->submitted)
		{
			return false;
		}
		
		$this->valid = true;
		
		foreach($this->fields as $field)
		{
			$value = $this->request[$field[0]];
			
			if(isset($field[2]['optional']) && $field[2]['optional'])
			{
				if($value == '') continue;
			}
			
			foreach($field[2] as $validator=>$data)
			{
				if($validator == 'optional') continue;
				
				$custom = !method_exists($this, $validator);
				
				if((!$custom && !$this->$validator($value, $data)) || ($custom && !$this->custom($data, $value)))
				{
					$this->valid = false;
					
					$this->errors[$field[0]] = $this->error_message($field[0], $validator, $data);
					
					break;
				}
			}
		}
		
		return $this->valid?$this->request:false;
	}
	
	// Validators
	private function custom($validator, $value)
	{
		return call_user_func($validator, $value, $this->request, $this->id);
	}
	
	private function min_length($value, $min)
	{
		return strlen($value) >= $min;
	}
	
	private function max_length($value, $max)
	{
		return strlen($value) <= $max;
	}
	
	private function alphanumeric($value, $custom)
	{
		return preg_match('/^(['.$custom.'a-z0-9_]*)$/i', $value);
	}
	
	private function valid_email($value)
	{
		return preg_match('/^\S+@\S+\.\S+$/', $value);
	}
	
	private function confirm($value, $field)
	{
		return $value == $this->request[$field];
	}
}